package com.ruoyi.business.common.riskRule;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.service.*;
import com.ruoyi.business.Vo.RulePremiseVo;
import com.ruoyi.business.service.impl.BaseService;
import com.ruoyi.common.constant.RiskConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.Func;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.bizsys.domain.LoanOrder.LOAN_STATUS_9_APPROVALING;
import static com.ruoyi.bizsys.utils.BizConstants.APPLY_STATUS_9_APPROVALING;
import static com.ruoyi.common.constant.RiskConstants.RULE_TYPE_APPLY;
import static com.ruoyi.common.constant.RiskConstants.RULE_TYPE_LOAN;

@Component
@Slf4j
public class RulePremiseService extends BaseService {

    @Autowired
    protected ICreditFlagService creditFlagService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IDeviceAppListService iDeviceAppListService;

    @Autowired
    private ICreditApplyService creditApplyService;

    @Autowired
    private ICreditAccountService creditAccountService;

    @Autowired
    private ILoanOrderService loanOrderService;

    @Autowired
    private ICustReplenishInfoService replenishInfoService;

    @Autowired
    private IApplyContactsService iApplyContactsService;

    @Autowired
    private ICustomerInfoService customerInfoService;

    @Autowired
    IDeviceContactsService iDeviceContactsService;

    @Autowired
    private IAppDeviceContactapplistRelativeService iAppDeviceContactapplistRelativeService;

    @Autowired
    private IAppDeviceInfoService iAppDeviceInfoService;

    @Autowired
    private IAppDeviceInfoService appDeviceInfoService;

    private Map<String, RuleStrategy> map;

    public static final String RULE_PREMISE_LOCK = "rulePremiseLock:";

    private static Set checkTypes = new HashSet();

    public RulePremiseService(List<RuleStrategy> ruleStrategies) {
        checkTypes.add(RiskConstants.RULE_TYPE_APPLY);
        checkTypes.add(RiskConstants.RULE_TYPE_LOAN);
        checkTypes.add(RiskConstants.RULE_TYPE_REPLENISH);
        this.map = ruleStrategies.stream().collect(Collectors.toMap(RuleStrategy::getType, i -> i));
    }

    public RuleStrategy getRuleStrategy(String type) {
        RuleStrategy ruleStrategy = map.get(type);
        if (ruleStrategy == null) {
            throw new BusinessException(RspEnmu.SYSTEM_EXCEPTION);
        }
        return map.get(type);
    }

    @Async("threadPoolTaskExecutor")
    public void rulePremise(RulePremiseVo rulePremiseVo, String... columnK) {
        try {
            log.info("前置规则校验：RulePremiseVo：{},columK:{}", rulePremiseVo.toString(), columnK);

            if (!checkTypes.contains(rulePremiseVo.getType())) return;
            //orderIsApprovalCheck(rulePremiseVo);
            RuleStrategy ruleStrategy = getRuleStrategy(rulePremiseVo.getType());
            ruleStrategy.rulePremise(this, rulePremiseVo, columnK);
        } catch (Exception e) {
            log.error("rulePremise异常：", e);
        }
        log.info("规则调用结束");
    }


    //设备数据
    @Async("threadPoolTaskExecutor")
    public void deviceRulePremise(String custNo, String applyNo, String columnK) {
        log.info("前置规则校验：deviceRulePremise，cust:{},applyNo:{},columnK：{}", custNo, applyNo, columnK);
        //获取客户审批中订单
        RLock v228Lock = getV228Lock(custNo);
        try {
            List<CreditFlag> approvalFlags = creditFlagService.getApprovalFlags(custNo);
            approvalFlags.forEach(e -> {

                if (!creditFlagColumnCheck(e, columnK)) return;

                CreditFlag flag = new CreditFlag();
                flag.setId(e.getId());
                flag.setSerialNo(e.getSerialNo());
                if ("appsFlag".equals(columnK)) {
                    flag.setAppsFlag(1);
                    flag.setAppRelationNo(applyNo);
                }
                if ("contactsFlag".equals(columnK)) {
                    flag.setContactsFlag(1);
                    flag.setContactsRelationNo(applyNo);
                }
                creditFlagService.updateById(flag);

                //都为空
                if (fieldsHasEmpty(e)) return;

                new Thread(() -> {
                    log.info("客户：{},columnK:{},流水号：{},type:{} 开始走风控", custNo, columnK, e.getSerialNo(), e.getType());
                    RulePremiseVo rulePremiseVo = RulePremiseVo.builder().custNo(custNo).serialNo(e.getSerialNo()).build();
                    getRuleStrategy(e.getType()).passToRisk(rulePremiseVo);
                }).start();

            });
        } catch (Exception e) {
            log.error("deviceRulePremise异常：", e);
        } finally {
            v228Lock.unlock();
        }
    }

    private boolean creditFlagColumnCheck(CreditFlag e, String type) {
        if ("appsFlag".equals(type) && (Func.isEmpty(e.getAppsFlag()) || e.getAppsFlag() != 1)) {
            e.setAppsFlag(1);
            return true;
        }
        if ("contactsFlag".equals(type) && (Func.isEmpty(e.getContactsFlag()) || e.getContactsFlag() != 1)) {
            e.setContactsFlag(1);
            return true;
        }
        return false;
    }


    @Async("threadPoolTaskExecutor")
    public void rulePremiseV228(RulePremiseVo rulePremiseVo) {
        try {
            log.info("前置规则校验：RulePremiseVo：{}", rulePremiseVo.toString());

            //获取类型和流水号
            // v228InitSerialNoAndType(rulePremiseVo);
            if (!checkTypes.contains(rulePremiseVo.getType())) return;
            RuleStrategy ruleStrategy = getRuleStrategy(rulePremiseVo.getType());
            ruleStrategy.rulePremiseV228(this, rulePremiseVo);
        } catch (Exception e) {
            log.error("rulePremise异常：", e);
        }
        log.info("规则调用结束");
    }


    /**
     * 标识校验
     *
     * @return
     */
    public boolean operationFlag(RulePremiseVo rulePremiseVo, String... columnK) {

        CreditFlag creditFlag = null;
        CreditFlag sourceFlag = null;
        String serialNo = rulePremiseVo.getSerialNo();
//        String lockKey = RULE_PREMISE_LOCK + serialNo;
        RLock lock = getV228Lock(rulePremiseVo.getCustNo());
        try {
            //初始设值
            creditFlag = buildCreateCreditFlag(rulePremiseVo.getSerialNo(), columnK);

            //是否已存在
            sourceFlag = getCreditFlag(serialNo);

            //新增或修改
            creditFlag.setUpdateTime(new Date());
            creditFlag.setType(rulePremiseVo.getType());
            creditFlag.setCustNo(rulePremiseVo.getCustNo());

            //不存在->新增
            if (Func.isEmpty(sourceFlag)) {
                creditFlag.setCreateTime(new Date());
                creditFlag = flagProcess(rulePremiseVo, creditFlag);
                creditFlagService.insertCreditFlag(creditFlag);

            } else {
                //判断是否已经结束 或 当前 flag 已经标记
                if (!fieldsHasEmpty(sourceFlag) || !flagged(sourceFlag, columnK[0])) {
                    return false;
                }
                creditFlag = flagProcess(rulePremiseVo, creditFlag);
                creditFlag.setId(sourceFlag.getId());
                creditFlagService.updateById(creditFlag);
            }

        } catch (Exception e) {
            log.error("operationFlag 异常：", e);
        } finally {
            log.info("释放标识校验锁.");
            lock.unlock();
        }
        sourceFlag = combineCreditFlagCore(sourceFlag, creditFlag);
        // 略过日历（非必需）
        return !(fieldsHasEmpty(sourceFlag) || "calendarFlag".equals(columnK[0]));
    }

    public boolean operationFlagV228(RulePremiseVo rulePremiseVo) {

        CreditFlag creditFlag = null;
        RLock lock = getV228Lock(rulePremiseVo.getCustNo());

        try {
            //只新增
//            creditFlag = getCreditFlag(rulePremiseVo.getSerialNo());
            //初始设值
            creditFlag = buildCreateCreditFlagV228(rulePremiseVo);
            creditFlagService.insertCreditFlag(creditFlag);
            //结束
        } catch (Exception e) {
            log.error("operationFlag 异常：", e);
        } finally {
            log.info("释放标识校验锁.");
            lock.unlock();
        }
        // 略过日历（非必需）
        return !(fieldsHasEmpty(creditFlag));
    }

    private RLock getV228Lock(String custNo) {
        return redisCache.lock(RULE_PREMISE_LOCK + custNo);
    }


    /**
     * 兼容处理
     * v228 获取用户当前在操作的流水号和类型
     *
     * @param rulePremiseVo
     */
    private void v228InitSerialNoAndType(RulePremiseVo rulePremiseVo) {
        log.info("v228InitSerialNoAndType,custNo:{},", rulePremiseVo.getCustNo());
//        boolean credit = creditAccountService.isCredit(rulePremiseVo.getCustNo());
//        rulePremiseVo.setType(credit ? RuleStrategy.LOAN : RuleStrategy.CREDIT);
        orderIsApprovalCheck(rulePremiseVo);
    }

    private void orderIsApprovalCheck(RulePremiseVo rulePremiseVo) {
        boolean orderIsApproval = orderIsApproval(rulePremiseVo);
        if (!orderIsApproval) {
            log.warn("v228没有订单处于审核中custNO:{}", rulePremiseVo.getCustNo());
            throw new BusinessException("v228没有订单处于审核中");
        }
    }

    /**
     * 订单是否处于审核中
     *
     * @param rulePremiseVo
     * @param
     * @return
     */
    private boolean orderIsApproval(RulePremiseVo rulePremiseVo) {
        //审批中?
        if (RULE_TYPE_APPLY.equals(rulePremiseVo.getType())) {
            CreditApply creditApply = creditApplyService.getOneByCust(rulePremiseVo.getCustNo());
            if (Func.isNotEmpty(creditApply) && APPLY_STATUS_9_APPROVALING == creditApply.getApplyStatus()) {
                rulePremiseVo.setSerialNo(creditApply.getApplySerialNo());
                return true;
            }
        }
        if (RULE_TYPE_LOAN.equals(rulePremiseVo.getType())) {
            LoanOrder loanOrder = loanOrderService.getOneByCustNo(rulePremiseVo.getCustNo());
            if (Func.isNotEmpty(loanOrder) && LOAN_STATUS_9_APPROVALING == loanOrder.getLoanStatus()) {
                rulePremiseVo.setSerialNo(loanOrder.getLoanSerialNo());
                return true;
            }
        }
        if (RuleStrategy.REPLENISH.equals(rulePremiseVo.getType())) {
            CustReplenishInfo replenishInfo = replenishInfoService.getOneByCustNo(rulePremiseVo.getCustNo());
            if (Func.isNotEmpty(replenishInfo) && CustReplenishInfo.APPROVAL.equals(replenishInfo.getState())) {
                rulePremiseVo.setSerialNo(replenishInfo.getSerialNo());
                return true;
            }
        }
        return false;
    }

    private CreditFlag getCreditFlag(String serialNo) {
        QueryWrapper<CreditFlag> eq = new QueryWrapper<CreditFlag>().eq("serial_no", serialNo).last("order by id desc limit 1");
        return creditFlagService.getOne(eq);
    }


    /**
     * setContactsRelationNo  setAppRelationNo
     *
     * @param rulePremiseVo
     * @param creditFlag
     * @return
     */
    public CreditFlag flagProcess(RulePremiseVo rulePremiseVo, CreditFlag creditFlag) {
        log.info("前置校验是否SSSSet:{},{}", rulePremiseVo.toString(), creditFlag.toString());
        QueryWrapper<CustomerInfo> queryWrapper = new QueryWrapper<CustomerInfo>().eq("cust_no", rulePremiseVo.getCustNo()).select("user_id");
        String userId = customerInfoService.getOne(queryWrapper).getUserId();
        String tableName = iApplyContactsService.getTableName(userId);
        String sourceFingerprint = iAppDeviceInfoService.getDeviceIdByUserId(userId, creditFlag.getCreateTime());

        // 授信
        if (RiskConstants.RULE_TYPE_APPLY.equals(rulePremiseVo.getType())) {
            if (StringUtils.isBlank(creditFlag.getContactsRelationNo())) {
                int contactsCount = 0;
                if (StringUtils.isNotBlank(tableName)) {
                    contactsCount = iApplyContactsService.selectContactsCount(userId, rulePremiseVo.getSerialNo(), tableName);
                } else {
                    String deviceTableName = iDeviceContactsService.getTableName(sourceFingerprint);
                    contactsCount = iDeviceContactsService.selectContactsCount(sourceFingerprint, rulePremiseVo.getSerialNo(), deviceTableName);
                }
                if (contactsCount > 0) {
                    creditFlag.setContactsFlag(1);
                    creditFlag.setContactsRelationNo(rulePremiseVo.getSerialNo());
                }
            }

            if (StringUtils.isBlank(creditFlag.getAppRelationNo())) {
                int applistCount = iDeviceAppListService.selectApplistCount(sourceFingerprint, rulePremiseVo.getSerialNo());
                if (applistCount > 0) {
                    creditFlag.setAppsFlag(1);
                    creditFlag.setAppRelationNo(rulePremiseVo.getSerialNo());
                }
            }
        }

        // 提现
        if (RiskConstants.RULE_TYPE_LOAN.equals(rulePremiseVo.getType())) {
            // 如果 Contacts 关联流水号不为空，且存在 7 天内最近的 Contacts 流水号，则直接关联赋值。applist 同理
            boolean consistent = getRuleStrategy(rulePremiseVo.getType()).fingerprintsSame(sourceFingerprint, rulePremiseVo.getSerialNo(), rulePremiseVo.getCustNo());

            if (StringUtils.isBlank(creditFlag.getContactsRelationNo())) {
                String contactsApplyNo = "";
                if (StringUtils.isNotBlank(tableName)) {
                    contactsApplyNo = iApplyContactsService.get7DaysContactsCount(tableName, userId);
                } else {
                    String deviceTableName = iDeviceContactsService.getTableName(sourceFingerprint);
                    contactsApplyNo = iDeviceContactsService.get7DaysContactsCount(deviceTableName, sourceFingerprint);
                }
                if (consistent && StringUtils.isNotBlank(contactsApplyNo)) {
                    creditFlag.setContactsFlag(1);
                    creditFlag.setContactsRelationNo(contactsApplyNo);
                }
                //设备指纹不一致
                if (!consistent) {
                    creditFlag.setContactsRelationNo(rulePremiseVo.getSerialNo());
                }
            }
            if (StringUtils.isBlank(creditFlag.getAppRelationNo())) {
                String appApplyNo = iDeviceAppListService.get7DaysAppList(sourceFingerprint);
                if (consistent && StringUtils.isNotBlank(appApplyNo)) {
                    creditFlag.setAppsFlag(1);
                    creditFlag.setAppRelationNo(appApplyNo);
                }
                //设备指纹不一致
                if (!consistent) {
                    creditFlag.setAppRelationNo(rulePremiseVo.getSerialNo());
                }
            }

        }
        return creditFlag;
    }

    public CreditFlag flagProcessV228(String deviceId, String userId, CreditFlag creditFlag) {
        String contactNo = iAppDeviceContactapplistRelativeService.getBy7DdayData(deviceId, userId, 1);
        String appNo = iAppDeviceContactapplistRelativeService.getBy7DdayData(deviceId, userId, 2);
        if (StringUtils.isNotBlank(contactNo)) {
            creditFlag.setContactsFlag(1);
            creditFlag.setContactsRelationNo(contactNo);
        }
        if (StringUtils.isNotBlank(appNo)) {
            creditFlag.setAppsFlag(1);
            creditFlag.setAppRelationNo(appNo);
        }
        return creditFlag;
    }


    /**
     * 新增授信7日有效的 提现标识记录
     *
     * @param loanNo
     */
    public void saveLoanRelationFlag(String loanNo, String custNo) {
        QueryWrapper<CustomerInfo> queryWrapper = new QueryWrapper<CustomerInfo>().eq("cust_no", custNo).select("user_id");
        String userId = customerInfoService.getOne(queryWrapper).getUserId();

        RLock v228Lock = getV228Lock(custNo);
        try {
            CreditFlag creditFlag = buildCreateCreditFlag(loanNo, "deviceFlag", "orderFlag", "contactsFlag", "appsFlag", "calendarFlag");
            String sourceFingerprint = iAppDeviceInfoService.getDeviceIdByUserId(userId, DateUtils.getNowDate());
            String tableName = iApplyContactsService.getTableName(userId);
            String contactsApplyNo = "";
            String appApplyNo = "";
            if (StringUtils.isNotBlank(tableName)) {
                //最终提交 更新通讯录 app 标识
                contactsApplyNo = iApplyContactsService.get7DaysContactsCount(tableName, userId);
            } else {
                String deviceTableName = iDeviceContactsService.getTableName(sourceFingerprint);
                contactsApplyNo = iDeviceContactsService.get7DaysContactsCount(deviceTableName, sourceFingerprint);
            }
            appApplyNo = iDeviceAppListService.get7DaysAppList(sourceFingerprint);
            creditFlag.setAppRelationNo(appApplyNo);
            creditFlag.setType(RuleStrategy.LOAN);
            creditFlag.setContactsRelationNo(contactsApplyNo);
            creditFlag.setCustNo(custNo);

            boolean update = creditFlagService.update(creditFlag, new UpdateWrapper<CreditFlag>().eq("serial_no", loanNo));
            if (!update) {
                creditFlagService.insertCreditFlag(creditFlag);
            }
        } catch (Exception e) {
            log.error("saveLoanRelationFlag异常", e);
        } finally {
            log.info("释放锁");
            v228Lock.unlock();
        }
    }

    /**
     * 校验 订单，设备信息，通讯录，AppList是否已标识
     *
     * @param creditFlag
     * @return
     */
    private boolean fieldsHasEmpty(CreditFlag creditFlag) {
        return Func.hasEmpty(creditFlag.getOrderFlag(), creditFlag.getDeviceFlag(), creditFlag.getAppsFlag(), creditFlag.getContactsFlag());
    }

    private boolean flagged(CreditFlag creditFlag, String column) {
        if ("appsFlag".equals(column)) {
            return Func.isEmpty(creditFlag.getAppsFlag());
        }
        if ("orderFlag".equals(column)) {
            return Func.isEmpty(creditFlag.getOrderFlag());
        }
        if ("contactsFlag".equals(column)) {
            return Func.isEmpty(creditFlag.getContactsFlag());
        }
        return true;
    }

    /**
     * CreditFlag初始化
     *
     * @return
     */
    private CreditFlag buildCreateCreditFlag(String serialNo, String... columnK) {
        CreditFlag creditFlag = new CreditFlag();
        creditFlag.setSerialNo(serialNo);
        Integer columnV = 1;
        try {
            for (int i = 0; i < columnK.length; i++) {
                try {
                    String methodName = columnK[i].substring(0, 1).toUpperCase() + columnK[i].substring(1);
                    Method setMethod = CreditFlag.class.getMethod("set" + methodName, Integer.class);
                    setMethod.invoke(creditFlag, columnV);
                } catch (Exception e) {
                    log.info("方法不存在：{}", columnK[i]);
                }
            }
        } catch (Exception e) {
            log.error("buildCreateCreditFlag 异常：", e);
        }
        return creditFlag;
    }

    private CreditFlag buildCreateCreditFlagV228(RulePremiseVo rulePremiseVo) {
        CreditFlag creditFlag = new CreditFlag();
        creditFlag.setSerialNo(rulePremiseVo.getSerialNo());
        creditFlag.setType(rulePremiseVo.getType());
        creditFlag.setCustNo(rulePremiseVo.getCustNo());
        creditFlag.setCreateTime(new Date());
        creditFlag.setUpdateTime(new Date());
        String contactNo = iAppDeviceContactapplistRelativeService.getBy7DdayData(rulePremiseVo.getFingerprint(), rulePremiseVo.getUserId(), 1);
        String appNo = iAppDeviceContactapplistRelativeService.getBy7DdayData(rulePremiseVo.getFingerprint(), rulePremiseVo.getUserId(), 2);
        if (StringUtils.isNotBlank(contactNo)) {
            creditFlag.setContactsFlag(1);
            creditFlag.setContactsRelationNo(contactNo);
        }
        if (StringUtils.isNotBlank(appNo)) {
            creditFlag.setAppsFlag(1);
            creditFlag.setAppRelationNo(appNo);
        }
        creditFlag.setDeviceFlag(1);
        creditFlag.setOrderFlag(1);
        return creditFlag;
    }

    private static CreditFlag combineCreditFlagCore(CreditFlag sourceBean, CreditFlag targetBean) {
        if (sourceBean == null && targetBean == null) {
            return new CreditFlag();
        }
        if (sourceBean == null) {
            return targetBean;
        }
        if (targetBean == null) {
            return sourceBean;
        }
        Class sourceBeanClass = sourceBean.getClass();
        Field[] sourceFields = sourceBeanClass.getDeclaredFields();
        Field[] targetFields = sourceBeanClass.getDeclaredFields();
        for (int i = 0; i < sourceFields.length; i++) {
            Field sourceField = sourceFields[i];
            Field targetField = targetFields[i];
            sourceField.setAccessible(true);
            targetField.setAccessible(true);
            try {
                if (!"serialVersionUID".equals(sourceField.getName()) && !(sourceField.get(sourceBean) == null)) {
                    targetField.set(targetBean, sourceField.get(sourceBean));
                }
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return targetBean;
    }

    public void reviewCreditFlagAgain(CreditFlag creditFlag, String userId) {
        //如果 appList 或 通讯录为空
        if (Func.hasEmpty(creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
            log.info("为空-----AppsFlag:{};ContactsFlag:{}", creditFlag.getAppsFlag(), creditFlag.getContactsFlag());
            //重新跑一下
            RulePremiseVo rulePremiseVo = new RulePremiseVo().builder().custNo(creditFlag.getCustNo()).serialNo(creditFlag.getSerialNo()).type(creditFlag.getType()).build();
            creditFlag = this.flagProcess(rulePremiseVo, creditFlag);
            //更新appList
            String deviceIdByUserId = appDeviceInfoService.getDeviceIdByUserId(userId, creditFlag.getCreateTime());
            creditFlag = this.flagProcessV228(deviceIdByUserId, userId, creditFlag);
            if (!Func.hasEmpty(creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
                log.info("修改-----AppsFlag:{};ContactsFlag:{}", creditFlag.getAppsFlag(), creditFlag.getContactsFlag());
                creditFlagService.updateCreditFlag(creditFlag);
            }
        }

    }
}
